/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://code.google.com/p/dylab/

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_SimplicalComplex_h__
#define __dylab_SimplicalComplex_h__

//===========================================================================================
#include <dylab/utils/EnumIterator.h>
#include <dylab/utils/random/RandomValue.h>
#include <dylab/utils/containers/HashObjContainer.h>
#include <dylab/utils/containers/ListObjContainer.h>

//===========================================================================================
namespace dylab {

    /** Simplical complex of any given order.     
     */    
    class DYLAB_EXPORT SimplicalComplex
    {
    public:
        /// _simplex of the simplical complex of any order (n-_simplex, where 0 <= n <= MAX_ORDER)
        class DYLAB_EXPORT Simplex
        {
        public:
            /// supported orders of simplices in this complex
            enum order_t
            { 
                ORDER_0 = 0,
                ORDER_1,
                ORDER_2,
                ORDER_3
            };
            /// define iterator for order_t enumeration
            class OrderIterator : public EnumIterator<order_t, ORDER_0, ORDER_3> { };
            /// max supported order
            enum { MAX_ORDER = ORDER_3 };
            /// number of supported orders
            enum { ORDER_COUNT = MAX_ORDER + 1 };

        public:
            /// type of a _simplex's identification
            typedef uint32_t id_t;            

        public:
            // hash map containing some "other" simplices - does not own them!
            class DYLAB_EXPORT OtherSimplices
                : public HashObjContainer<id_t, Simplex> 
            { 
            public:
                OtherSimplices()
                    : HashObjContainer<id_t, Simplex>(false)
                        { }
            };
            //-----------------------------------------------------------------------------

        private:
            /// identification of this _simplex
            id_t mId;
            /// order of this _simplex
            order_t mOrder; 
            /// parent simplical complex
            SimplicalComplex * mParentComplex;
            /// sub- & super-simplces
            OtherSimplices mSubSimplices;
            OtherSimplices mSuperSimplices;
            /// indicates, whether to destruct its sub-boundaries when destructed
            bool mRemoveSubBoundaries;

        public:

            /** default constructor
             */
            Simplex(order_t order, id_t id, SimplicalComplex * complex, bool _removeSubBoundaries = true, bool _notifyComplex = true);

            /** 
             */
            virtual ~Simplex();

            /**
             */
            inline id_t getId() const
                    { return mId; }  

            /** 
             */
            inline order_t getOrder() const
                    { return mOrder; }

            /** 
             */
            inline SimplicalComplex * getComplex()
                    { return mParentComplex; }

            /** 
             */
            inline const SimplicalComplex * getComplex() const
                    { return mParentComplex; }

            /** 
             */
            inline OtherSimplices & getSubSimplices()
                    { return mSubSimplices; }

            /** 
             */
            inline const OtherSimplices & getSubSimplices() const
                    { return mSubSimplices; }

            /** 
             */
            inline OtherSimplices & getSuperSimplices()
                    { return mSuperSimplices; }

            /** 
             */
            inline const OtherSimplices & getSuperSimplices() const
                    { return mSuperSimplices; }

            /** 
             */
            inline bool isSubSimplex(Simplex * _simplex) const
                    {   
                        DYLAB_ASSERT(_simplex != NULL);
                        return order_t(mOrder - 1) == _simplex->getOrder();
                    }

            /** 
             */
            inline bool isSuperSimplex(Simplex * _simplex) const
                    {   
                        DYLAB_ASSERT(_simplex != NULL);
                        return order_t(mOrder + 1) == _simplex->getOrder();
                    }

            /**
             */
            inline bool isBoundary() const
                    { return mSuperSimplices.getCount() == 1; }

            /**
             */
            inline bool isSuperBoundary() const
                    { 
                        // TODO - return true if any of its super-simplices is boundary
                        return false;
                    }

            /**
             */
            void connect(Simplex * _simplex);

            /**
             */
            void disconnect(Simplex * _simplex);

        private:

            /**
             */
            void _attachSimplex(Simplex * _simplex);

            /**
             */
            void _detachSimplex(Simplex * _simplex);
        };    
        //-----------------------------------------------------------------------------

    public:
        // simple hash map
        class DYLAB_EXPORT Simplices 
            : public HashObjContainer<Simplex::id_t, Simplex> 
        { 
        public:
            Simplices()
                : HashObjContainer<Simplex::id_t, Simplex>(true)
                    { }
        };
        //-----------------------------------------------------------------------------

    public:
        /// base class for sub- and super-adjacent _simplex map generation
        /// all generated simplices must be of the same order specified at the construction
        class DYLAB_EXPORT AdjacentSimplices
        {
        public:
            /// adjacent simplex map structure
            typedef Simplex::OtherSimplices Simplices;
        private:
            Simplices mSimplices;
            Simplex::order_t mOrder;
        public:

            /** deafult constructor
             */
            AdjacentSimplices(Simplex::order_t order);

            /** destroys the map
             */
            virtual ~AdjacentSimplices();

            /** destroys the map
             */
            Simplex::order_t getOrder() const
                    { return mOrder; }

            /** 
             */
            Simplices::Iterator simplicesIterator()
                    { return Simplices::Iterator(mSimplices); }

            /** 
             */
            Simplices::ConstIterator simplicesIterator() const
                    { return Simplices::ConstIterator(mSimplices); }

            /** Merges the _simplex into the simplices
             */
            void mergeSimplex(Simplex * _simplex);

            /** Merges the simplices between the beginIterator (inclusive) and endIterator into the simplices, 
                but withou _simplex with id == excludeID.
             */
            void mergeSimplices(const Simplices & _simplices, Simplex::id_t excludeId = 0);

            /** Merges all sub-adjacent simplices (of the same order)
             */
            void mergeSubAdjacentSimplices(Simplex * _sourceSimplex);

            /** merges all super-adjacent simplices (of the same order)
             */
            void mergeSuperAdjacentSimplices(Simplex * _sourceSimplex);

            /** Merges all (directly) adjacent sub-simplices
             */
            void mergeAdjacentSubSimplices(Simplex * _sourceSimplex);

            /** merges all (directly) adjacent super-simplices
             */
            void mergeAdjacentSuperSimplices(Simplex * _sourceSimplex);

            /** Merges the simplices between the beginIterator (inclusive) and endIterator into the simplices, 
                but withou _simplex with id == excludeID.
             */
            inline void mergeSubSimplices(const Simplex * _simplex)
                    { 
                        DYLAB_ASSERT(_simplex != NULL);
                        mergeSimplices(_simplex->getSubSimplices());
                    }

            /** merges all sub-adjacent simplices (of the same order) of the simplices 
                between the beginIterator (inclusive) and endIterator
             */
            inline void mergeSubAdjacentSimplices(Simplices & _simplices)
                    {
                        for (Simplices::Iterator it(_simplices); it.isValid(); it++)
                            mergeSubAdjacentSimplices(it());
                    }
            /** merges all super-adjacent simplices (of the same order) of the simplices 
                between the beginIterator (inclusive) and endIterator
             */
            inline void mergeSuperAdjacentSimplices(Simplices & _simplices)
                    {
                        for (Simplices::Iterator it(_simplices); it.isValid(); it++)
                            mergeSuperAdjacentSimplices(it());
                    }
            /** merges all adjacent sub-simplices (of the same order) of the simplices 
                between the beginIterator (inclusive) and endIterator
             */
            inline void mergeAdjacentSubSimplices(Simplices & _simplices)
                    {
                        for (Simplices::Iterator it(_simplices); it.isValid(); it++)
                            mergeAdjacentSubSimplices(it());
                    }
            /** merges all adjacent super-simplices (of the same order) of the simplices 
                between the beginIterator (inclusive) and endIterator
             */
            inline void mergeAdjacentSuperSimplices(Simplices & _simplices)
                    {
                        for (Simplices::Iterator it(_simplices); it.isValid(); it++)
                            mergeAdjacentSuperSimplices(it());
                    }
            /** 
             */
            inline void clear()
                    { mSimplices.removeAll(); }
        };
        //-----------------------------------------------------------------------------

    public:
        /// _simplex listener (i.e., insertion x deletion of simplices) interface
        class DYLAB_EXPORT ISimplexListener
        {
        public:
            virtual ~ISimplexListener()
                    { }
            virtual void onSimplexAdded(Simplex * _simplex) = 0;
            virtual void onSimplexRemoved(Simplex * _simplex) = 0;
            virtual ISimplexListener * clone() const = 0;
        };
        //-----------------------------------------------------------------------------

    public:
        // _simplex listeners - not owning the pointers - on destruction only remove
        class DYLAB_EXPORT SimplexListeners 
            : public ListObjContainer<ISimplexListener> 
        { 
        public:
            SimplexListeners()
                : ListObjContainer<ISimplexListener>(false)
                    { }
        };
        //-----------------------------------------------------------------------------

    private:
        Simplices mSimplices[Simplex::ORDER_COUNT];
        SimplexListeners mSimplexListeners[Simplex::ORDER_COUNT];       
        RandomValue<uint32_t> mIdGenerator;

    public:

        /** Default constructor.
         */
        SimplicalComplex();

        /** Destructor.
         */
        virtual ~SimplicalComplex();

        /**
        */
        Simplex::id_t generateSimplexID(Simplex::order_t _order);

        /**
        */
        void destroySimplex(Simplex::order_t _order, Simplex::id_t _simplexId);

        /**
        */
        inline Simplices::Iterator simplicesIterator(Simplex::order_t _order)
                { return Simplices::Iterator(mSimplices[_order]); }

        /**
        */
        inline Simplices::Reader simplicesReader(Simplex::order_t _order) const
                { return Simplices::Reader(mSimplices[_order]); }

        /**
        */
        inline SimplexListeners & getSimplexListeners(Simplex::order_t _order)
                { return mSimplexListeners[_order]; }

    protected:

        /**
        */
        void _notifySimplexCreated(Simplex * _simplex);
    };    
}
//===========================================================================================
#endif // __dylab_SimplicalComplex_h__
